package com.ruoyi.project.app.controller;

import com.alibaba.fastjson.JSONObject;
import com.lry.anno.ExceptionListener;
import com.ruoyi.framework.interceptor.annotation.RepeatSubmit;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.project.app.domain.Msg;
import com.ruoyi.project.app.domain.Order;
import com.ruoyi.project.app.mapper.OrderMapper;
import com.ruoyi.project.app.mapper.MsgMapper;
import com.ruoyi.project.app.mapper.OrderMapper;
import com.ruoyi.project.app.mapper.ProductMapper;
import com.ruoyi.project.app.service.UserService;
import com.ruoyi.project.app.service.impl.UserServiceImpl2;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.DispatcherServlet;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("user")
@Api("用户登录注册")
@Slf4j
public class UserController extends BaseController{

    @Autowired
    public UserService userService;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private ProductMapper productMapper;

    Random random = new Random();

    @Resource
    private MergeQueue mergeQueue;


    @GetMapping("/submitOrder")
    public void submitOrder() throws InterruptedException {
        Order order = new Order();
        int userId = random.nextInt(4)+1;//1-4
        int productId= random.nextInt(10)+1;//1-2
        int buyNum = random.nextInt(5)+1;//1-5
        order.setUserId(userId);
        order.setProductId(productId);
        order.setBuyNum(buyNum);

        MergeQueue.Result result = mergeQueue.offer(order);

        if(!result.isSuccess()){
            log.info("秒杀失败,result="+result);
        }
    }



    @PostMapping("/addMsg")
    public void addMsg(@RequestBody Msg msg){
        System.out.println(msg);
    }

    //
    @GetMapping("/msg")
    public Msg msg(){
        Msg msg = new Msg(1,"2", LocalDateTime.now());
        return msg;
    }


    @GetMapping("/test")
    @RepeatSubmit
    public void test1(@RequestBody JSONObject obj){
        //测试重复提交
    }




    //事务失效
    @RequestMapping("pub")
    public void pub()  {
        userService.pub();
    }

    //事务失效
    @RequestMapping("testTran")
    public void testTran()  {
        Order order = new Order();
        order.setName("order1");
        Msg msg = new Msg();
        msg.setMsg("hello world");
        userService.testTran(order,msg);
    }

    //事务失效
    @RequestMapping("tran")
    public void tran(){
        Order order = new Order();
        order.setName("order1");
        userService.createOrder(order);
    }


    //异步失效
    @RequestMapping("async")
    public void async(){
        userService.testAsync();
    }


    @RequestMapping("testConn")
//    @ExceptionListener
    public String testConn(){
        System.out.println(Thread.currentThread().getName());
        throw new RuntimeException("lalalal");
//        return "conn";
    }


    @Autowired
    private RedissonClient redissonClient;

    //缓存正好失效了
    @RequestMapping("updateByLock")
    public void updateByLock(){
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(10, TimeUnit.SECONDS);
            //先更新数据库
            try {
                //模拟更新，耗时500ms
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //在删除缓存
            redisCache.deleteObject(goodsKey);
        }finally {
            lock.unlock();
        }
    }

    @RequestMapping("getByLock")
    public void getByLock(){
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(10, TimeUnit.SECONDS);
            //先从缓存拿
            Object obj = redisCache.getCacheObject(goodsKey);

            //缓存没有再从数据库拿
            if(null==obj){
                try {
                    //模拟读库200ms，读的是旧的数据
                    Thread.sleep(200);
                    obj = "goods";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    //模拟GC 1000ms
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //最后回写缓存
                redisCache.setCacheObject(goodsKey,obj);
            }

        }finally {
            lock.unlock();
        }
    }



    //缓存正好失效了
    @RequestMapping("updateByReadWriteLock")
    public void updateByReadWriteLock(){
        RReadWriteLock lock = redissonClient.getReadWriteLock(lockKey);
        try {
            lock.writeLock().lock(10, TimeUnit.SECONDS);
            //先更新数据库
            try {
                //模拟更新，耗时500ms
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //在删除缓存
            redisCache.deleteObject(goodsKey);
        }finally {
            lock.writeLock().unlock();
        }
    }

    @RequestMapping("getByReadWriteLock")
    public void getByReadWriteLock(){
        String str ="dsa";
        int i = str.split(".").length;
        RReadWriteLock lock = redissonClient.getReadWriteLock(lockKey);
        try {
            lock.readLock().lock(10, TimeUnit.SECONDS);
            //先从缓存拿
            Object obj = redisCache.getCacheObject(goodsKey);

            //缓存没有再从数据库拿
            if(null==obj){
                try {
                    //模拟读库200ms，读的是旧的数据
                    Thread.sleep(200);
                    obj = "goods";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    //模拟GC 1000ms
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //最后回写缓存
                redisCache.setCacheObject(goodsKey,obj);
            }

        }finally {
            lock.readLock().unlock();
        }
    }


    @RequestMapping("update")
    public void update(){
        //先更新数据库
        try {
            //模拟更新，耗时500ms
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在删除缓存
        redisCache.deleteObject(goodsKey);
    }

    @RequestMapping("get")
    public void get(){

        //先从缓存拿
        Object obj = redisCache.getCacheObject(goodsKey);

        //缓存没有再从数据库拿
        if(null==obj){
            try {
                //模拟读库200ms，读的是旧的数据
                Thread.sleep(200);
                obj = "goods";
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                //模拟GC 1000ms
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //最后回写缓存
            redisCache.setCacheObject(goodsKey,obj);
        }

    }



    @Autowired
    RedisCache redisCache;
    String goodsKey = "goodsId:1";
    String lockKey = "lock:" + goodsKey;
    String goodsKeyVersion = goodsKey+":version";

    @RequestMapping("updateByCas")
    public void updateByCas(){
        //先更新数据库
        try {
            //模拟更新，耗时500ms
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //在删除缓存
        redisCache.deleteObject(goodsKey);
        updateVersion();
    }

    private Integer updateVersion(){
        //版本字段
        Integer version = redisCache.getCacheObject(goodsKeyVersion);
        if(version==null){
            version = 1;
        }else {
            version++;
        }
        redisCache.setCacheObject(goodsKeyVersion,version);
        return version;
    }

    private boolean canReWrite(int cur){
        Integer version = redisCache.getCacheObject(goodsKeyVersion);
        if(null==version)
            return true;

        if(cur>=version)
            return true;

        return false;
    }

    @RequestMapping("getByCas")
    public void getByCas(){

        //先从缓存拿
        Object obj = redisCache.getCacheObject(goodsKey);

        //缓存没有再从数据库拿
        if(null==obj){
            Integer version = updateVersion();
            try {
                //模拟读库200ms，读的是旧的数据
                Thread.sleep(200);
                obj = "goods";
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                //模拟GC 1000ms
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(canReWrite(version)){
                //最后回写缓存
                redisCache.setCacheObject(goodsKey,obj);
            }

        }

    }




    //分布式锁，乐观锁，读写锁，可重入锁，红锁（其实就是实现了一个过半机制的强一致性而已），redssion
    //zookeeper: 天然强一致性，做分布式锁的极佳选择

    //前提：redis设置过期时间
    //最终一致性

    //定时刷库：数据量大，开线程池开销，可能需要mq
    //延时双删：延时任务开销，可能还需要mq
    //分布式锁：串行化，效率低，安全性很高，集群环境可能由于主节点down机但是锁还没同步给从节点，导致出现错误
    //乐观锁：效率高，安全性会有问题，集群环境也有问题
    //读写锁: 效率高，集群环境也有问题

}
